import { observe } from "./observer/index";
import { isPlainObject } from "./util/util";
import Watcher from "./observer/watcher";
import Dep from "./observer/dep";
/**
 * 初始化实现Vue中的方法
 * 这的实现方式都是面试题
 */
export function initState(vm) {
  const opts = vm.$options || {};
  // console.log("opts", opts,vm);
  if (opts.props) initProps(vm, opts.props);
  if (opts.methods) initMethods(vm, opts.methods);
  if (opts.data) {
    initData(vm);
  } else {
    // observe((vm._data = {}), true);
  }
  if (opts.computed) initComputed(vm, opts.computed);
  if (opts.watch) {
    initWatch(vm, opts.watch);
  }
}
function initProps() {}
function initMethods() {}

// Vue2对data初始化
function initData(vm) {
  let data = vm.$options.data;
  // 判断当前的data是对象还是方法
  // 1.当data被重新赋值以后指向就成了windown 所以打印data() 里面的this指向会变成windown
  // vm.$options.data = typeof vm.$options.data === "function" ? vm.$options.data() : {};
  data = vm._data = typeof data === "function" ? data.call(vm) : {};

  // 把data上所有的数据都挂在到vm实例上
  for (const key in data) {
    proxy(vm, `_data`, key)
  }

  // 对数据进行观测
  observe(data)
  
}

function initComputed(vm) {
  let computed = vm.$options.computed
  // console.log('[ computed ] >', computed.funName())
  // 1.需要一个watcher
  let watcher = vm._computedWatchers = {}
  // console.log('[ vm ] >', vm)
  for (const key in computed) {
    // console.log('[ key ] >', key)
    // 注意：这个地方可能是对象也可能是方法
    /**
     * funName:{  里面有方法 多种写法
     *  get(){},
     *  set(){}
     * }
     */
    let userDef = computed[key]
    // 判断当前的是不是一个函数
    let getter = typeof userDef == 'function'?userDef:userDef.get
    // 给我们的每一个计算属性都要添加一个watcher
    watcher[key] = new Watcher(vm,getter,()=>{},{lazy:true})
    // 通过Object.defineProperty对数据进行劫持
    defineComputed(vm,key,userDef)
  }
}
let sharedPropDefinition = {}
function defineComputed(target,key,userDef){
    sharedPropDefinition ={
      enumerable:true, //枚举
      configurable:true, //可获取
      get:()=>{},
      set:()=>{}
    }
    if(typeof userDef == 'function'){
      sharedPropDefinition.get = createComutedGetter(key)
    }else{
      sharedPropDefinition.get = createComutedGetter(key)
      sharedPropDefinition.set = userDef.set
    }
    Object.defineProperty(target,key,sharedPropDefinition)
}
//高阶函数的意思就是值得参数就是一个函数 或者返回值是一个函数
function createComutedGetter(key){ // 返回的是用户的方法
    return function(){
      // console.log('[ this ] >', this)
      // 谁调用就获取到谁的
      let watcher = this._computedWatchers[key]
      if(watcher){
        if(watcher.dirty){
          // 求值  在watcher重新定义一个方法
          watcher.evaluate()  // 就是走用户的方法

        }
        if(Dep.target){ // 说明现在还有未渲染的watcher
          watcher.depend() //计算属性wathcer
        }
        return watcher.value
      }

    }
}





function initWatch(vm,watch) {
  // watch 有4种基本使用 里面可以为对象也可以是数组
  // console.log('[ vminitWatch ] >', vm,watch)
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {  // 数组
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else { // 对象，字符串，函数
      createWatcher(vm, key, handler)
    }
  }
}

function createWatcher(
  vm,
  expOrFn,
  handler,
  options
) {

  // 是否是对象
  if (isPlainObject(handler)) {
    options = handler // 用户的一些配置项
    handler = handler.handler // 得到了函数下的handler
  }
  // 是否是字符串
  if (typeof handler === 'string') {
    handler = vm[handler]  // 如果是字符串的那个那么就去实例上面找 一定是在实例上面定义了这个方法 不然规矩不允许
  }
              //   表达式，   函数 ，   配置项
  // console.log('[ vm2 ] >', vm)
  return vm.$watch(expOrFn, handler, options)
}

function proxy(target,source,key){
  Object.defineProperty(target, key,{
    get(){
      return target[source][key]
    },
    set(newValue){
      target[source][key] = newValue
    }
  })
}
export function stateMixin(Vue) {
  // $watch的实现方式
  // 1. 格式化  2. 在通过watch进行操作
  Vue.prototype.$watch = function (
    expOrFn,
    cb,
    options
  ) {
    // console.log('[ expOrFn,cb,options ] >', options)
    const vm = this
    // if (isPlainObject(cb)) {
    //   return createWatcher(vm, expOrFn, cb, options)
    // }
    // options = options || {}
    // options.user = true
    // 视频这是四个参数 官方文档是三个                 代表是用户的watcher 这是只有用户才会调用$watch
    const watcher = new Watcher(vm, expOrFn, cb, {...options,user:true})
    if (options.immediate) { 
      // const info = `callback for immediate watcher "${watcher.expression}"`
      cb.call(Vue)
      // pushTarget()
      // invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
      // popTarget()
    }
    // return function unwatchFn() {
    //   watcher.teardown()
    // }
  }
  // console.log('[ Vue1 ] >', Vue.prototype)

}
